﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection.Emit;

namespace Aeon.Emulator.Decoding.Recompiler
{
    internal sealed class Conditions
    {
        private readonly RegisterExpressions registers;

        public Conditions(RegisterExpressions registers)
        {
            this.registers = registers;
        }

        public void GenerateJump(string name, int addressSize, Label target, ILGenerator il)
        {
            switch(name)
            {
            case "jo":
                AndFlags(EFlags.Overflow, il);
                il.Emit(OpCodes.Brtrue, target);
                break;

            case "jno":
                AndFlags(EFlags.Overflow, il);
                il.Emit(OpCodes.Brfalse, target);
                break;

            case "jc":
                AndFlags(EFlags.Carry, il);
                il.Emit(OpCodes.Brtrue, target);
                break;

            case "jnc":
                AndFlags(EFlags.Carry, il);
                il.Emit(OpCodes.Brfalse, target);
                break;

            case "jz":
                AndFlags(EFlags.Zero, il);
                il.Emit(OpCodes.Brtrue, target);
                break;

            case "jnz":
                AndFlags(EFlags.Zero, il);
                il.Emit(OpCodes.Brfalse, target);
                break;

            case "jcz":
                AndFlags(EFlags.Carry | EFlags.Zero, il);
                il.Emit(OpCodes.Brtrue, target);
                break;

            case "jncz":
                AndFlags(EFlags.Carry | EFlags.Zero, il);
                il.Emit(OpCodes.Brfalse, target);
                break;

            case "js":
                AndFlags(EFlags.Sign, il);
                il.Emit(OpCodes.Brtrue, target);
                break;

            case "jns":
                AndFlags(EFlags.Sign, il);
                il.Emit(OpCodes.Brfalse, target);
                break;

            case "jl":
                SignNotOverflow(target, il);
                break;

            case "jge":
                SignOverflow(target, il);
                break;

            case "jle":
                AndFlags(EFlags.Zero, il);
                il.Emit(OpCodes.Brtrue, target);
                SignNotOverflow(target, il);
                break;

            case "jg":
                JumpGreaterThan(target, il);
                break;

            case "jp":
                AndFlags(EFlags.Parity, il);
                il.Emit(OpCodes.Brtrue, target);
                break;

            case "jnp":
                AndFlags(EFlags.Parity, il);
                il.Emit(OpCodes.Brfalse, target);
                break;

            case "jcxz":
                if(addressSize == 16)
                    this.registers.GenerateValue("CX", il);
                else
                    this.registers.GenerateValue("ECX", il);
                il.Emit(OpCodes.Brfalse, target);
                break;

            default:
                throw new ArgumentException();
            }
        }

        private static void JumpGreaterThan(Label target, ILGenerator il)
        {
            AndFlags(EFlags.Sign | EFlags.Overflow | EFlags.Zero, il);
            il.Emit(OpCodes.Brfalse, target);
            AndFlags(EFlags.Sign | EFlags.Overflow | EFlags.Zero, il);
            il.LoadConstant((int)(EFlags.Sign | EFlags.Overflow));
            il.Emit(OpCodes.Ceq);
            il.Emit(OpCodes.Brtrue, target);
        }
        private static void SignNotOverflow(Label target, ILGenerator il)
        {
            AndFlags(EFlags.Sign | EFlags.Overflow, il);
            il.LoadConstant((int)EFlags.Sign);
            il.Emit(OpCodes.Ceq);
            il.Emit(OpCodes.Brtrue, target);
            AndFlags(EFlags.Sign | EFlags.Overflow, il);
            il.LoadConstant((int)EFlags.Overflow);
            il.Emit(OpCodes.Ceq);
            il.Emit(OpCodes.Brtrue, target);
        }
        private static void SignOverflow(Label target, ILGenerator il)
        {
            AndFlags(EFlags.Sign | EFlags.Overflow, il);
            il.Emit(OpCodes.Brfalse, target);
            AndFlags(EFlags.Sign | EFlags.Overflow, il);
            il.LoadConstant((int)(EFlags.Sign | EFlags.Overflow));
            il.Emit(OpCodes.Ceq);
            il.Emit(OpCodes.Brtrue, target);
        }
        public static void AndFlags(EFlags flags, ILGenerator il)
        {
            il.LoadArgument(0);
            il.Emit(OpCodes.Ldfld, Infos.VirtualMachine.Processor);
            il.Emit(OpCodes.Ldfld, Infos.Processor.Flags);
            il.LoadConstant((int)flags);
            il.Emit(OpCodes.And);
        }
    }
}
